import java.awt.*;
import java.util.*;

/**
 * @author LKQ
 * @date 2022/5/26 10:45
 * @description 线段树数据结构，常用来维护区间信息，可以在O(logN)的时间复杂度内实现以下功能：
 * 单点修改、区间修改、区间查询（区间求和、求区间最大值、求区间最小值）等操作
 * 下面的方法采用线段树 动态开点 - 动态指针的方式，用数组保存线段树，具体转换方法可以查看算法基础简介
 */
public class SegmentTree {

    public static int N = (int) 1e5;
    public static int[] data = new int[N * 4];
    public static int[] tr = new int[N * 4], tag = new int[N * 4];

    public int ls(int p) {
        // 左节点
        return p << 1;
    }
    public int rs(int p) {
        // 右节点
        return p << 1 | 1;
    }

    public void pushUp(int p) {
        // 向上不断维护区间操作
        tr[p] = tr[ls(p)] + tr[rs(p)];
    }

    /**
     * 如果涉及到维护区间最大值，最小值问题，可以加上。
     * @param p 节点
     */
    public void pushUpMin(int p) {
        // 向上维护当前节点区间的最小值
        // min[p] = Math.min(min[ls(p)], min[rs(p)]);
    }
    public void pushUpMax(int p) {
        // max[p] = Math.max(max[ls(p)], max[rs(p)]);
    }

    public void build(int p, int l, int r) {
        if (l == r) {
            // 如果左右区间相同，那么必然是叶子节点，只有叶子节点是被真实赋值的
            tr[p] = data[l];
            return;
        }
        int mid = l + ((r - l) >> 1);
        build(ls(p), l, mid);
        build(rs(p), mid + 1, r);
        // 通过子节点来维护父节点，pushUp位置在最后
        pushUp(p);
    }

    public void f(int p, int l, int r, int k) {
        if (k == 0) {
            return;
        }
        // 节点p代表的区间 [l, r]每个数都加 k，打上懒加载标记
        tag[p] += k;
        // 区间和变化
        tr[p] += k * (r - l + 1);

    }
    public void pushDown(int p, int l, int r) {
        int mid = l + ((r - l) >> 1);
        // 将懒加载值向下传递
        f(ls(p), l, mid, tag[p]);
        f(rs(p), mid + 1, r, tag[p]);
        // 传递完毕后置0
        tag[p] = 0;
    }

    /**
     * 修改区间 [s, t]内每个数 + k。
     * @param p 当前节点 p
     * @param l p存储的区间 [l, r]
     * @param r p存储的区间 [l, r]
     */
    public void update(int s, int t, int l, int r, int p, int k) {
        if (s <= l && r <= t) {
            // 当前区间为修改区间的子集
            tr[p] += k * (r - l + 1);
            tag[p] += k;
            return;
        }
        // 回溯之前，更新子节点
        pushDown(p, l, r);

        int mid = l + ((r - l) >> 1);
        if (s <= mid) {
            update(s, t, l, mid, ls(p), k);
        }
        if (t > mid) {
            update(s, t, mid + 1, r, rs(p), k);
        }
        // 回溯之后
        pushUp(p);

    }

    /**
     * 查询区间 [x, y]的区间和、最大值，最小值
     * @param l p节点存储的区间 [l, r]
     * @param r p节点存储的区间 [l, r]
     * @param p p节点
     * @return 查询值
     */
    public int query(int x, int y, int l, int r, int p) {
        int res = 0;
        if (x <= l && r <= y) {
            return tr[p];
        }
        int mid = l + ((r - l) >> 1);
        pushDown(p, l , r);
        if (x <= mid) {
            res += query(x, y, l, mid, ls(p));
        }
        if (y > mid) {
            res += query(x, y, mid + 1, r, rs(p));
        }
        return res;
    }


    public static void main(String[] args) {
        SegmentTree segmentTree = new SegmentTree();

        // 第一行包含两个整数 n, m，分别表示该数列数字的个数和操作的总个数
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        sc.nextLine();
        // 第二行包含n个由空格分割的整数，其中第i个数字表示数列第i项的初始值
        for (int i = 1; i <= n; i++) {
            data[i] = sc.nextInt();
        }
        sc.nextLine();
        // 接下来m行，每行包含3/4个整数，表示一种操作 1 x y k, 表示 将区间[x, y]内每个数加k, 2 x y 表示输出【x, y】内每个数的和

        segmentTree.build(1, 1, n);

        while (m-- > 0) {
            int op = sc.nextInt();
            switch (op) {
                case 1:
                    int x = sc.nextInt(), y = sc.nextInt(), k = sc.nextInt();
                    segmentTree.update(x, y, 1, n, 1, k);
                    break;
                case 2:
                    int a = sc.nextInt(), b = sc.nextInt();
                    System.out.println(segmentTree.query(a, b, 1, n, 1));
                    break;
                default:
                    break;
            }
            sc.nextLine();
        }
    }
}
